#!/usr/bin/env python
#-*- encoding:utf-8 -*-
import conf
import urllib2,urllib
import httplib
import urlparse
import time
import threading
import os
import traceback
import MySQLdb
import logging
import socket
import re
import BaseHTTPServer
import Queue
import thread
import xmlrpclib
import time
from threadpool import ThreadPool, WorkRequest
socket.setdefaulttimeout(30)


#################global ####################
#管理进程
g_manage = ""
#数据库读
g_dbw = ""
#数据库写
g_dbr = ""

#共享队列相关
g_queue_sleep = 1
g_queue_size = 2000
g_queue = Queue.Queue(maxsize = g_queue_size)

#采集对象相关，中间用ID进行关联
g_collect = {}

#最大线程个数
g_thread_num = 10
#################logging ###################
logging.basicConfig(level=logging.DEBUG,
                format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                datefmt='%a, %d %b %Y %H:%M:%S',
                filename='./monitor.log',
                filemode='w')

def getUrlData(url):
    data = ""
    try:
        fd = urllib2.urlopen(url)
        data = fd.read()
        code = 200
        fd.close()
    except:
        code = False    
    return {"data": data, 'code': code}
 
def detectHttp(url):
    try:
        (proto, host, path, x1, param, x2) = urlparse.urlparse(url)
        conn = httplib.HTTPConnection(host)
        if param:
            conn.request("GET","%s?%s" % (path, param))
        else:
            conn.request("GET", path)
        res = conn.getresponse()
        conn.close()
        if res.status == 200:
            return True
        else:
            print "http error: ", res.status, "url:", url
            return False
    except:
        print "http error"
        return False

def detectPort(host, port):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        s.connect((str(host), int(port)))
        s.send("monitor test")
        s.close()
        return True
    except socket.error, msg:
        return False

def getHostData(url):
    ret = {'code': -1, 'data': ''} 
    try:
        print url
        ip = url.replace('host://', '') 
        link = "http://%s:%d" % (ip, conf.g_rpc_port)
        print link
        server = xmlrpclib.ServerProxy(link)
        info = server.host.get_info()
        del server
        cpu = "%.3f" % info['cpu_usage'] 
        cpu_cores = info['cpu_cores'] 
        mem = "%.3f" % (info['mem_usage'] * info['mem_total'])
        mem_total = info['mem_total']
        disk = info['disk']
        disk_total = info['disk_total']
        load = info['load'] 
        print info
        eth0= info['eth0'].split('|')
        eth1 = info['eth1'].split('|')
        t = time.strptime(info['uptime'], "%Y-%m-%d %H:%M:%S")
        uptime = int(time.mktime(t))
        os = info['os_version']
        ret['code'] = 200
        ret['data'] = "cpu:%s,memory:%s,disk:%s,load:%s,uptime:%s,os:%s,eth0-send:%s,eth0-recv:%s,eth1-send:%s,eth1-recv:%s,disk_total:%s,mem_total:%s,cpu_cores:%s" % \
                                (cpu, mem, disk, load,uptime, os,eth0[0],eth0[1],eth1[0],eth1[1], disk_total, mem_total, cpu_cores)
        print ret
        return ret 
    except:
        print "------------------------------"
        print link
        print "------------------------------"
        traceback.print_exc()
        return ret

def idExist(id):
    flag = False
    sql = "select id from monitor_monitor where id=%d and open='1'" % int(id)
    ret = g_dbr.executeSqlRet(sql)
    if ret:
        flag = True
    return flag

def url2str(url):
    return "[%s]" % url['mname']

def print_result(request, result):
        print "**** Result from request #%s: %r" % (request.requestID, result)

def handle_exception(request, exc_info):
    if not isinstance(exc_info, tuple):
        # Something is seriously wrong...
        print request
        print exc_info
        raise SystemExit
    print "**** Exception occured in request #%s: %s" % \
      (request.requestID, exc_info)

class DbObject:
    
    #数据库是共享互斥异步的
    def __init__(self, host, user, passwd, database, port=3306, charset="utf8", dict=False):
        self._conn = MySQLdb.connect(host=host, user=user, passwd=passwd, \
                        db=database, port=port, charset=charset)
        if dict:
            self._cursor = self._conn.cursor(cursorclass = MySQLdb.cursors.DictCursor)
        else:
            self._cursor = self._conn.cursor()
        self._cursor.execute("SET NAMES 'utf8'")
        self._l = threading.Lock()

    def executeSql(self ,sql):
        self._l.acquire()
        try:
            sql = sql.encode("utf-8")
            res = self._cursor.execute(sql)
            logging.info("execute: %s, status: %s" % (sql, res))
        except:
            traceback.print_exc()
            res = False
        #finally:
        self._l.release()
        return res
     
    def executeSqlRet(self ,sql):
        self._l.acquire()
        try:
            sql = sql.encode("utf-8")
            stat = self._cursor.execute(sql)
            logging.info("execute: %s, status: %s" % (sql, stat))
            res = self._cursor.fetchall()
        except:
            traceback.print_exc()
            res = False
        #finally:
        self._l.release()
        return res
     
    def __del__(self):
        self._cursor.close()


class Manage:
    
    def __init__(self, db):
        #data用来记录全局数据，记录每个进程的状态
        self._data = {}
        self._db = db
        self._lock = threading.Lock()
        self.queue =  g_queue 
        self.collect = g_collect
        self.tp = ThreadPool(g_thread_num)
        #启动工作线程
        thread.start_new(self.producer, ())
        thread.start_new(self.consumer, ())
        
    def status(self):
        info = ""
        for k, v in self._data.items():
            info += "ID:%s , Freq:%d \n" % (k, v[0])
        info += "Total %d Threads \n" % len(self._data)
        return info
        
    def register(self, id):
        self._lock.acquire()
        status = self._data.get(str(id), False)
        if status != False:
            ret = False
            logging.info("Thread Resiger Fail, ID: %d" % id)
        else:
            #设置时间触发器,同时生成采集对象
            co = Collecter(self._db, id)
            g_collect[str(id)] = co
            if co and co.status == True:
                self._data[str(id)] = [co.freq, int(time.time())]
                logging.info("Thread Resiger OK, ID: %d" % id)
                ret = True
            else:
                ret = False
                logging.info("Thread Resiger Fail, ID: %d" % id)
        self._lock.release()
        return ret
        
    def unregister(self, id):
        self._lock.acquire()
        status = self._data.get(str(id), False)
        if status == False:
            ret = False
        else:
            del self._data[str(id)]
            del g_collect[str(id)]
            ret = True
        self._lock.release()
        logging.info("Thread Unresiger OK, ID: %d" % id)
        return ret
    
    def isExist(self, id):
        return self._data.get(str(id), False)

    #生产者，不断往队列中输入
    def producer(self):
        while True:
            time.sleep(g_queue_sleep)

            #依次塞入队列,同时设置下次的发生时间,类似定时器
            self._lock.acquire()
            now = int(time.time())
            for k, v in self._data.items():
                freq = v[0]
                click_time = v[1]
                if click_time <= now:
                    self.queue.put(k)
                    self._data[k][1] = now + freq
                    if self.queue.qsize() > 0.8 * g_queue_size:
                            logging.info("queue is very long, size: %d" % self.queue.qsize())

            self._lock.release()

    #消费者，不断消费队列中的东西
    def consumer(self):
        while True:
            id = self.queue.get()
            task = self.collect[str(id)]
            req = WorkRequest(task.run, None, None ,  print_result, handle_exception)
            #req = WorkRequest(test, [id], None ,  print_result, handle_exception)
            self.tp.putRequest(req)
            print "put request ok, id: ", req.requestID
            #print "put request ok, id: ", str(id)
            

#收集数据，并且过滤处理，然后发送数据库
class Collecter:
    name = "Collecter"
    
    def __init__(self, dbr, id):
        print "Start %s, ID: %d..." % (self.name, id)
        #标志此类的存活，看能不能找到跟好的方法
        self.status = False
        self._dbr = dbr
        self._id = id

        #加载配置
        self._url = self.loadConfig(id, dbr)
        if not self._url:
            #加载配置失败
            logging.info("Load config error, ID: %d..." % id)
            del self
        else:
            if not self._url.has_key('data'):
                self._url['data'] = {"total": 0, "now": 0, "status": True}
            #创建采集模板
            self._tpl = Tpl(self._url)
            self.freq = self._url['freq']
            self.status = True
            logging.info("Start %s, ID: %d..." % (self.name, id))
        
    
    def loadConfig(self, id, db):
        url = {}
        #获取监控参数
        sql = "select * from monitor_monitor where id=%d and open ='T'" % id
        ret = db.executeSqlRet(sql)
        if ret:
            return  ret[0]
        else:
            return []
        
        #发送数据
    def sendData(self):
        #http://ip/service/senddata/?mid=id&time=12444444&data=cpu:12,mem:23
        #http://192.168.78.129:8080/monitor/senddata/?mid=2&time=12444444&data=-1
        mid = self._id
        timestamp = int(time.time())
        data = self._url['data']['now']
        reUrl = "%s/?mid=%s&time=%d&data=%s" % (conf.g_send_url, mid, timestamp, data)
        print reUrl
        fd = urllib.urlopen(reUrl)
        fd.read()
        fd.close()

    def run(self):
        self._tpl.process()
        self.sendData()

#格式定义
#nginx:
#apache:
#http:    http://ip/path
#host:    host://ip
#mysql:   mysql://ip:port
#tcp:     tcp://ip:port
#other    cmd://ip:cmd:path
class Tpl:
    name = "Tpl"
    
    def __init__(self, url):
        print "Start %s..." % self.name
        self._url = url
        if not url.has_key('data'):
            self._url['data'] = {"total": 0, "now": 0, "status": True}

    def process(self):
        url = self._url
        try:
            if url['tpl'] == 'host':
                self._host()
            elif url['tpl'] == 'nginx':
                self._nginx()
            elif url['tpl'] == 'apache':
                self._apache()
            elif url['tpl'] == 'port':
                self._port()
            elif url['tpl'] == 'http':
                self._http()
            elif url['tpl'] == 'other':
                self._cmd()
            else:

                print "unknown tpl"
        except:
            print url
            traceback.print_exc()
    

    def _nginx(self):
        url = self._url
        ret = getUrlData(url['url'])
        if ret['code'] != 200:
            url['data']['status'] = False
            total = url['data']['total']
            #now = 0
            now = -1
        else:
            url['data']['status'] = True
            total = int (ret['data'].split()[9])
            if url['data']['total'] == 0:
                now = 0
            else:
                now = total - url['data']['total']
        url['data']['total'] = total
        url['data']['now'] = now
        logging.info("url: %s, status: %s" % (url['url'], url['data']['status'] ))
        return True

    def _host(self):
        url = self._url
        ret = getHostData(url['url'])
        if ret['code'] != 200:
            url['data']['status'] = False
            now = -1
        else:
            url['data']['status'] = True
            now = ret['data']
        url['data']['total'] = 0
        url['data']['now'] = now
        logging.info("url: %s, status: %s" % (url['url'], url['data']['status'] ))
        return True
 
        
    def _apache(self):
        pass

    def _port(self):
        url = self._url
        (host, port) = url['url'].replace("tcp://", "").split(":")
        ret = detectPort(host, port)
        url['data']['status'] = ret
        url['data']['total'] = 0
        url['data']['now'] = int(ret)
        logging.info("url: %s, status: %s" % (url['url'], url['data']['status'] ))
        return 0

    def _http(self):
        url = self._url
        ret = detectHttp(url['url'])
        url['data']['status'] = ret
        url['data']['total'] = 0
        url['data']['now'] = int(ret)
        logging.info("url: %s, status: %s" % (url['url'], url['data']['status'] ))
        return 0

    def _cmd(self):
        url = self._url
        dct = {}
        cmd = "%s/%s" % (conf.bin, self._url['url'])
        fd = os.popen(cmd)
        try:
            for line in fd.readlines():
                (key, value) = line.strip("\n").split(":")
                dct[key.lower()] = value
            if dct['status'] == "1":
                url['data']['status'] = True
                url['data']['now'] = float(dct['value'])
            else:
                url['data']['status'] = False
                url['data']['now'] = 0
                
            url['data']['total'] = 0
        except:
            url['data']['status'] = False
            url['data']['total'] = 0
            url['data']['now'] = 0
        finally:
            fd.close()
        logging.info("url: %s, status: %s" % (url['url'], url['data']['status'] ))
        return 0
        
class MinHandler(BaseHTTPServer.BaseHTTPRequestHandler):

    def do_HEAD(self):
        self.send_response(200)
        self.send_header("Content-type", "text/plain")
        self.end_headers()
        
    def do_GET(self):
        """Respond to a GET request."""
        print self.path
        if self.path == "/status":
            msg = g_manage.status()
            self.send_response(200)
            self.end_headers()
            self.wfile.write(msg)
            return 
        
        match = re.findall("/([a-z]+)/(\d+)", self.path) 
        if not match:
            self.send_response(403)
            self.end_headers()
        else:
            (cmd, id) = match[0]
            id = int(id)
            if cmd == "start":
                msg = self.start(id)
            elif cmd == "stop":
                msg = self.stop(id)
            elif cmd == "reload":
                msg = self.reload(id)
            else:
                msg = "Error Command"
            self.send_response(200)
            self.end_headers()
            self.wfile.write(msg)
            return 
    
   
    def start(self, id):
        ret = g_manage.isExist(id)
        if ret:
            return  "code:False|ID: %d Exist!" % id
        else:
            ret = g_manage.register(id)
            if ret:
                return  "code:True|ID: %d start ok" % id
            else:
                return  "code:False|ID: %d start error" % id
        
       
    def stop(self, id):
        ret = g_manage.unregister(id)
        if ret:
            return  "code:True|ID: %d exit ok" % id
        else:
            return  "code:False|ID: %d exit error" % id
    
    def reload(self, id):
        ret = g_manage.unregister(id)
        if not ret:
            return  "code:False|ID: %d exit error" % id
        ret = g_manage.register(id)
        if not ret:
            return  "code:False|ID: %d start error" % id
        else:
            return  "code:True|ID: %d reload ok" % id
            
        
#def main():
if __name__ == '__main__':
    from daemonize import startstop
    startstop(stdout = "/tmp/monitor-new.out.1", pidfile = "/tmp/monitor-new.1.pid")
    #获取所有的监控id
    db = conf.db
    g_dbr = DbObject(db['host'], db['user'], db['passwd'], db['database'], db['port'], dict=True)
    sql = "select id, freq from monitor_monitor where open='T'"
    urls = g_dbr.executeSqlRet(sql)

    #创建管理进程
    g_manage = Manage(g_dbr)
    print "start register urls, total number: %d" % len(urls)
    for elem in urls:
        id = elem['id']
        g_manage.register(id)
        print "register id: %d ok" % id
    
    (host, port) = conf.listen.split(':')
    httpd = BaseHTTPServer.HTTPServer((host, int(port)), MinHandler)
    print "start http server at %s"  % conf.listen
    httpd.serve_forever()
   
